// Copyright (c) 2011 Blue Onion Software, All rights reserved
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace BlueOnionSoftware.Bloget
{
    public class BlogContainerControl : Control, INamingContainer
    {
        protected readonly Bloget Bloget;
        private readonly Blog _blog;

        public BlogContainerControl(Bloget bloget, Blog blog)
        {
            Bloget = bloget;
            _blog = blog;
            // ReSharper disable DoNotCallOverridableMethodsInConstructor
            EnableViewState = false;
            // ReSharper restore DoNotCallOverridableMethodsInConstructor
        }

        /// <summary>
        /// The titleNode of the Blog
        /// </summary>
        public string BlogTitle
        {
            get { return _blog.Title; }
        }

        /// <summary>
        /// Brief text of the blog. Sometimes referred to as a subtitle
        /// </summary>
        public string BlogDescription
        {
            get { return _blog.Description; }
        }

        /// <summary>
        /// The copyright of the blog
        /// </summary>
        public string BlogCopyright
        {
            get { return _blog.Copyright; }
        }

        /// <summary>
        /// The email address of the webmaster
        /// </summary>
        public string BlogWebmaster
        {
            get { return _blog.Webmaster; }
        }

        /// <summary>
        /// Returns true if logged in
        /// </summary>
        public bool BlogLoggedIn
        {
            get { return Bloget.IsLoggedIn; }
        }

        /// <summary>
        /// Returns the current view mode
        /// </summary>
        public Mode BlogMode
        {
            get { return Bloget.Mode; }
        }
    }

    public class IndexContainerControl : BlogContainerControl
    {
        protected readonly Post Post;

        /// <summary>
        /// Initializes a new instance of the <see cref="IndexContainerControl"/> class.
        /// </summary>
        /// <param name="bloget">The bloget control.</param>
        /// <param name="blog">The blog model being used.</param>
        /// <param name="post">The actual post.</param>
        public IndexContainerControl(Bloget bloget, Blog blog, Post post)
            : base(bloget, blog)
        {
            Post = post;
        }

        /// <summary>
        /// Gets the post ID. It is expressed as GUID in string format.
        /// </summary>
        /// <value>The post ID.</value>
        [SuppressMessage("Microsoft.Naming", "CA1706:ShortAcronymsShouldBeUppercase", MessageId = "Member")]
        public string PostId
        {
            get { return Post.Id.ToString(); }
        }

        /// <summary>
        /// Gets the post title.
        /// </summary>
        /// <value>The post title.</value>
        public string PostTitle
        {
            get { return Post.Title; }
        }

        /// <summary>
        /// Gets the post title URL encoded.
        /// </summary>
        /// <value>The post title URL encoded.</value>
        [SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings")]
        public string PostTitleUrlEncoded
        {
            get { return Bloget.Page.Server.UrlEncode(Post.Title); }
        }

        /// <summary>
        /// Gets the post created.
        /// </summary>
        /// <value>The post created.</value>
        public DateTime PostCreated
        {
            get { return Time.LocalTime(Bloget.Blog.TimeZone, Post.Created); }
        }

        /// <summary>
        /// Gets the post link.
        /// </summary>
        /// <value>The post link.</value>
        public string PostLink
        {
            get { return Bloget.BuildQueryString(Mode.Post, Post, 0, null, null, DateTime.MinValue, null); }
        }

        /// <summary>
        /// Gets the post edit link.
        /// </summary>
        /// <value>The post edit link.</value>
        public string PostEditLink
        {
            get { return Bloget.BuildQueryString(Mode.Edit, Post, 0, null, null, DateTime.MinValue, null); }
        }

        /// <summary>
        /// Gets the post perma link.
        /// </summary>
        /// <value>The post perma link.</value>
        public string PostPermaLink
        {
            get { return Bloget.PermaLink(Post); }
        }

        /// <summary>
        /// Gets the post perma link URL encoded.
        /// </summary>
        /// <value>The post perma link URL encoded.</value>
        [SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings")]
        public string PostPermaLinkUrlEncoded
        {
            get { return Bloget.Page.Server.UrlEncode(Bloget.PermaLink(Post)); }
        }

        /// <summary>
        /// Gets the post tags.
        /// </summary>
        /// <value>The post tags.</value>
        public IEnumerable<string> PostTags
        {
            get
            {
                return Array.AsReadOnly((
                    from tagInt in Post.Tags
                    where Bloget.Blog.Categories.Contains(tagInt)
                    select Bloget.Blog.Categories[tagInt].Name).ToArray());
            }
        }

        /// <summary>
        /// Posts the tag links.
        /// </summary>
        /// <param name="separator">The separator.</param>
        /// <returns>A string with the tags for the current posting separated by "separator"</returns>
        public string PostTagLinks(string separator)
        {
            using (var stringWriter = new StringWriter(CultureInfo.CurrentCulture))
            {
                using (var html = new HtmlTextWriter(stringWriter))
                {
                    var first = true;

                    foreach (var tagInt in Post.Tags.Where(tagInt => Bloget.Blog.Categories.Contains(tagInt)))
                    {
                        html.Write((first) ? Environment.NewLine : separator);
                        first = false;

                        var category = Bloget.Blog.Categories[tagInt];
                        var hyperLink = new HyperLink
                        {
                            NavigateUrl = Bloget.BuildQueryString(Mode.Blog, null, 0, category, null, DateTime.MinValue, null),
                            Text = category.Name
                        };
                        hyperLink.RenderControl(html);
                    }

                    html.Flush();
                    return stringWriter.ToString();
                }
            }
        }

        /// <summary>
        /// Displays posts related to this post.
        /// </summary>
        /// <param name="maxPosts">Maximum number of related posts to display</param>
        /// <returns></returns>
        public string RelatedPosts(int maxPosts)
        {
            if (maxPosts < 0)
            {
                return string.Empty;
            }

            if (maxPosts > Bloget.Blog.Posts.Count)
            {
                maxPosts = Bloget.Blog.Posts.Count;
            }

            var links = Bloget.RelatedPosts(Post, maxPosts);

            using (var stringWriter = new StringWriter(CultureInfo.CurrentCulture))
            using (var html = new HtmlTextWriter(stringWriter))
            {
                foreach (var link in links)
                {
                    link.RenderControl(html);
                    html.WriteLine("<br />");
                }

                html.Flush();
                return stringWriter.ToString();
            }
        }
    }

    public class PostContainerControl : IndexContainerControl
    {
        private readonly Content _content;

        /// <summary>
        /// Initializes a new instance of the <see cref="PostContainerControl"/> class.
        /// </summary>
        /// <param name="bloget">The bloget control.</param>
        /// <param name="blog">The blog model.</param>
        /// <param name="post">The posting.</param>
        /// <param name="content">The content of the posting.</param>
        public PostContainerControl(Bloget bloget, Blog blog, Post post, Content content)
            : base(bloget, blog, post)
        {
            _content = content;
        }

        /// <summary>
        /// Gets the content of the post.
        /// </summary>
        /// <value>The content of the post.</value>
        public string PostContent
        {
            get { return _content.Text; }
        }

        /// <summary>
        /// Gets an excerpt of the post. An excerpt is the first [length]
        /// characters in a post. All formatting and markup is removed.
        /// </summary>
        /// <param name="length">Max length of excerpt</param>
        /// <returns>Plain text of excerpt</returns>
        public string PostExcerpt(int length)
        {
            return SearchView.Excerpt(Bloget, Post, length);
        }

        /// <summary>
        /// Gets the post comment count.
        /// </summary>
        /// <value>The post comment count.</value>
        public int PostCommentCount
        {
            get { return _content.Comments.Count; }
        }

        /// <summary>
        /// Gets the post pingback count.
        /// </summary>
        /// <value>The post pingback count.</value>
        public int PostPingbackCount
        {
            get { return _content.References.Count; }
        }

        /// <summary>
        /// Gets the post source link.
        /// </summary>
        /// <value>The post source link.</value>
        public string PostSourceLink
        {
            get { return _content.SourceLink; }
        }

        /// <summary>
        /// Gets the post source title.
        /// </summary>
        /// <value>The post source title.</value>
        public string PostSourceTitle
        {
            get { return _content.SourceTitle; }
        }

        /// <summary>
        /// Gets the name of the post enclosure.
        /// </summary>
        /// <value>The name of the post enclosure.</value>
        public string PostEnclosureName
        {
            get { return (_content.Attachments.Count > 0) ? _content.Attachments[0].Name : string.Empty; }
        }

        /// <summary>
        /// Gets the size of the post enclosure name.
        /// </summary>
        /// <value>The size of the post enclosure name.</value>
        public string PostEnclosureNameSize
        {
            get { return (_content.Attachments.Count > 0) ? _content.Attachments[0].ToString() : string.Empty; }
        }

        /// <summary>
        /// Gets the post enclosure link.
        /// </summary>
        /// <value>The post enclosure link.</value>
        public string PostEnclosureLink
        {
            get { return (_content.Attachments.Count > 0) ? _content.Attachments[0].Url : string.Empty; }
        }

        /// <summary>
        /// Gets the type of the post enclosure.
        /// </summary>
        /// <value>The type of the post enclosure.</value>
        public string PostEnclosureType
        {
            get { return (_content.Attachments.Count > 0) ? _content.Attachments[0].MimeType : string.Empty; }
        }

        /// <summary>
        /// Gets the length of the post enclosure.
        /// </summary>
        /// <value>The length of the post enclosure.</value>
        public long PostEnclosureLength
        {
            get { return (_content.Attachments.Count > 0) ? _content.Attachments[0].Length : 0; }
        }
    }

    public class CommentContainerControl : PostContainerControl
    {
        private readonly Comment _comment;

        /// <summary>
        /// Initializes a new instance of the <see cref="CommentContainerControl"/> class.
        /// </summary>
        /// <param name="bloget">The bloget control.</param>
        /// <param name="blog">The blog model.</param>
        /// <param name="post">The posting.</param>
        /// <param name="content">The content of the posting.</param>
        /// <param name="comment">A comment of the posting.</param>
        public CommentContainerControl(Bloget bloget, Blog blog, Post post, Content content, Comment comment)
            : base(bloget, blog, post, content)
        {
            _comment = comment;
        }

        /// <summary>
        /// Gets the comment title.
        /// </summary>
        /// <value>The comment title.</value>
        public string CommentTitle
        {
            get { return _comment.Title; }
        }

        /// <summary>
        /// Gets the comment date.
        /// </summary>
        /// <value>The comment date.</value>
        public DateTime CommentDate
        {
            get { return Time.LocalTime(Bloget.Blog.TimeZone, _comment.Date); }
        }

        /// <summary>
        /// Gets the comment author.
        /// </summary>
        /// <value>The comment author.</value>
        public string CommentAuthor
        {
            get { return _comment.Author; }
        }

        /// <summary>
        /// Gets the comment email.
        /// </summary>
        /// <value>The comment email.</value>
        public string CommentEmail
        {
            get { return _comment.Email; }
        }

        /// <summary>
        /// Gets the comment text.
        /// </summary>
        /// <value>The comment text.</value>
        public string CommentText
        {
            get { return BlogView.CommentToHtml(_comment.Text); }
        }
    }

    public class PingBackContainerControl : PostContainerControl
    {
        private readonly Reference _pingBack;

        /// <summary>
        /// Initializes a new instance of the <see cref="PingBackContainerControl"/> class.
        /// </summary>
        /// <param name="bloget">The bloget.</param>
        /// <param name="blog">The blog.</param>
        /// <param name="post">The post.</param>
        /// <param name="content">The content.</param>
        /// <param name="pingBack">The ping back.</param>
        public PingBackContainerControl(Bloget bloget, Blog blog, Post post, Content content, Reference pingBack)
            : base(bloget, blog, post, content)
        {
            _pingBack = pingBack;
        }

        /// <summary>
        /// Gets the ping back link.
        /// </summary>
        /// <value>The ping back link.</value>
        public string PingBackLink
        {
            get { return _pingBack.Link; }
        }

        /// <summary>
        /// Gets the ping back date.
        /// </summary>
        /// <value>The ping back date.</value>
        [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "BackDate")]
        public DateTime PingBackDate
        {
            get { return Time.LocalTime(Bloget.Blog.TimeZone, _pingBack.Date); }
        }
    }
}